pages/[pageKey].tsx (238 lines of code) (raw):

import { useEffect, useContext, useMemo } from "react"; import Image from "next/image"; import { useRouter } from "next/router"; import Head from "next/head"; import { useStyletron } from "baseui"; import { PageContext } from "../components/layout"; import { Figma, LeftChevron, RightChevron } from "../components/icons"; import * as gtag from "../lib/gtag"; import { useSiblingPages } from "../lib/hooks"; import { ImageData, Page as PageT } from "../lib/types"; async function getStaticPaths() { const { getSiteMap } = require("../lib/api"); const siteMap = await getSiteMap(); const paths = []; for (const section of siteMap) { for (const page of section.children) { paths.push({ params: { pageKey: page.key }, }); } } return { paths, fallback: false }; } async function getStaticProps({ params }) { const { getSiteMap, getImage } = require("../lib/api"); const siteMap = await getSiteMap(); let activePage; for (const section of siteMap) { const match = section.children.find((page) => page.key === params.pageKey); if (match) { activePage = match; break; } } const image = await getImage(activePage); return { props: { image, siteMap, activePage, figmaLink: `https://www.figma.com/file/${activePage.fileKey}/${activePage.fileName}?node-id=${activePage.id}`, }, }; } function Page({ image }: { image: ImageData }) { const [css, theme] = useStyletron(); const { activePage, siteMap, figmaLink } = useContext(PageContext); // Scroll to top of page when image changes. useEffect(() => { window.scroll({ top: 0 }); }, [image]); const [previousPage, nextPage] = useSiblingPages(siteMap, activePage.key); const title = useMemo( () => `${activePage.sectionName} → ${activePage.name}`, [activePage] ); return ( <div className={css({ display: "flex", flexDirection: "column", maxWidth: "1280px", })} > <Head> <title>{title}</title> <meta property="og:title" content={title} key="title" /> </Head> <div className={css({ display: "flex", ...theme.borders.border300, borderWidth: "2px", background: theme.colors.white, marginBottom: theme.sizing.scale800, })} > <Image {...image} id="frame-image" key={activePage.key} alt={title} quality={100} /> </div> <div className={css({ display: "flex", flexWrap: "wrap", justifyContent: "space-between", [theme.mediaQuery.medium]: { flexWrap: "nowrap", }, })} > <SiblingPageButton page={previousPage} direction={-1} /> <SiblingPageButton page={nextPage} direction={1} /> <FigmaButton page={activePage} figmaLink={figmaLink} /> </div> </div> ); } function FigmaButton({ page, figmaLink }: { page: PageT; figmaLink: string }) { const [css, theme] = useStyletron(); return ( <a href={figmaLink} target="_blank" rel="noreferrer" title="Open in Figma (F)" onClick={() => { gtag.event({ action: "click_link_header_external", category: "navigation", label: page.key ? `figma_${page.key}` : "figma_root", }); }} className={css({ flex: 1, color: theme.colors.contentPrimary, background: theme.colors.backgroundTertiary, paddingLeft: theme.sizing.scale800, paddingRight: theme.sizing.scale800, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", order: 4, flexBasis: "100%", marginTop: theme.sizing.scale400, textDecoration: "none", outline: "none", transition: "background .2s ease-in-out", ":hover": { background: "#eaeaea", }, ":focus-visible": { boxShadow: "0 0 0 3px " + theme.colors.accent, }, [theme.mediaQuery.medium]: { order: 2, flexBasis: 0, marginTop: 0, marginLeft: theme.sizing.scale200, marginRight: theme.sizing.scale200, }, })} > <div> <Figma size="16px" /> </div> <div className={css({ marginLeft: theme.sizing.scale300, ...theme.typography.ParagraphSmall, color: theme.colors.contentSecondary, })} > Open in Figma </div> </a> ); } function SiblingPageButton({ page, direction, }: { page: PageT; direction: number; }) { const router = useRouter(); const [css, theme] = useStyletron(); return ( <div role="button" tabIndex={0} title={direction === -1 ? "Previous page (←)" : "Next page (→)"} onClick={() => router.push("/[pageKey]", `/${page.key}`)} className={css({ flex: 1, display: "flex", justifyContent: direction === -1 ? "default" : "flex-end", alignItems: "center", background: theme.colors.backgroundTertiary, height: "65px", width: "50%", cursor: "pointer", outline: "none", overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis", paddingRight: direction === -1 ? 0 : theme.sizing.scale600, paddingLeft: direction === -1 ? theme.sizing.scale600 : 0, marginRight: direction === -1 ? theme.sizing.scale200 : 0, marginLeft: direction === -1 ? 0 : theme.sizing.scale200, transition: "background .2s ease-in-out", ":hover": { background: "#eaeaea", }, ":focus-visible": { boxShadow: "0 0 0 3px " + theme.colors.accent, }, order: direction === -1 ? 1 : 3, })} > <div className={css({ order: direction === -1 ? "0" : "1", color: theme.colors.contentInverseTertiary, marginRight: direction === -1 ? theme.sizing.scale600 : 0, marginLeft: direction === -1 ? 0 : theme.sizing.scale600, })} > {direction === -1 ? <LeftChevron /> : <RightChevron />} </div> <div className={css({ textAlign: direction === -1 ? "left" : "right", })} > <div className={css({ ...theme.typography.LabelMedium, fontFamily: "UberMove", color: theme.colors.contentPrimary, })} > {page.sectionName} </div> <div className={css({ ...theme.typography.ParagraphSmall, color: theme.colors.contentTertiary, })} > {page.name} </div> </div> </div> ); } export { Page as default, getStaticPaths, getStaticProps };